#pragma once

#include <iostream>
#include <algorithm>
#include <unistd.h>
#include <mutex>
// #include "oj_model.hpp"
#include "oj_model_mysql.hpp"
#include "oj_view.hpp"

namespace ns_control
{
    using namespace ns_log;
        // 执行编译的主机
        class Machine
        {
        public:
            Machine()
            {}
            ~Machine(){}
        public:
            uint64_t GetLoad()
            {
                if(_mtx) _mtx->lock();
                uint64_t load = _load;
                if(_mtx) _mtx->unlock();
                return load;
            }
    
            void IncreaseLoad()
            {
                if(_mtx) _mtx->lock();
                ++_load;
                if(_mtx) _mtx->unlock();
            }
    
            void DecreaseLoad()
            {
                if(_mtx) _mtx->lock();
                --_load;
                if(_mtx) _mtx->unlock();
            }
    
            void ClearLoad()
            {
                if(_mtx) _mtx->lock();
                _load = 0;
                if(_mtx) _mtx->unlock();
            }
        public:
            std::string _ip;
            int _port;
            uint16_t _load = 0; // 负载数
            std::mutex *_mtx; // 管理_load的互斥锁
        };
    
        // 负载均衡
        class LoadBance
        {
        public:
            LoadBance()
            {
                assert(InitMachines()); // 初始化主机信息
            }
            ~LoadBance(){}
        public:
            // 获取负载最低的主机编号
            bool GetBestMachine(uint64_t* id, Machine** m)
            {
                _mtx.lock();
                if(_onLineMachines.size() == 0)
                {
                    LOG(FATAL) << "当前没有在线主机" << "\n";
                    _mtx.unlock();
                    return false;
                }
    
                // 从在线主机中选择负载最低的
                uint64_t min_load = _machines[_onLineMachines[0]].GetLoad();
                *id = _onLineMachines[0];
                *m = &_machines[*id];
                for(const auto& num : _onLineMachines)
                {
                    if(min_load > _machines[num].GetLoad())
                    {
                        *id = num;
                        *m = &_machines[num];
                    }
                }
                _mtx.unlock();
                return true;
            }
    
            void OnlineMachine()
            {
                // 将离线主机转移到在线主机中
                if(!_OffLineMachines.empty())
                {
                    for(auto id : _OffLineMachines) _onLineMachines.push_back(id);
                }
                _OffLineMachines.clear();
            }
    
            void OfflineMachine(int id)
            {
                auto it = _onLineMachines.begin();
                while(it != _onLineMachines.end())
                {
                    if(*it == id) // 下线主机
                    {
                        _machines[id].ClearLoad(); // 负载清零
                        _onLineMachines.erase(it);
                        _OffLineMachines.push_back(id);
                        break;
                    }
                    ++it;
                }
            }
            
        private:
            bool InitMachines()
            {
                // 加载所有主机信息到_machines中
                std::ifstream in(_MachineConfPath);
                if(!in.is_open())
                {
                    LOG(FATAL) << "加载主机配置文件失败" << "\n";
                    return false;
                }
    
                std::string line; // 127.0.0.1:8081
                while(std::getline(in, line))
                {
                    std::vector<std::string> tokens;
                    ns_util::FileUtil::StringSplit(line, tokens, ":"); // 分割
                    if(tokens.size() != 2)
                    {
                        LOG(FATAL) << "主机配置信息有错误" << "\n";
                        return false;
                    }
                    Machine m;
                    m._ip = tokens[0];
                    m._port = atoi(tokens[1].c_str());
                    m._mtx = new std::mutex();
                    _onLineMachines.push_back(_machines.size()); // 主机编号
                    _machines.push_back(m);
                }
                return true;
            }
        private:
            const std::string _MachineConfPath = "./machine.conf";
            std::vector<Machine> _machines; // 所有主机信息
            std::vector<int> _OffLineMachines; // 所有离线主机编号
            std::vector<int> _onLineMachines; // 所有在线主机编号
            std::mutex _mtx;
        };
    
    class Control
    {
    public:
        Control()
        {}
        ~Control()
        {}

    public:
        bool AllQuestions(std::string* html)
        {
            std::vector<ns_model::Question> v;
            if(_model.LoadAllQuestion(&v) == false) // 获取所有问题
            {
                // 加载失败
                LOG(FATAL) << "加载所有问题失败\n";
                return false;
            }

            // v排序
            std::sort(v.begin(), v.end(), [](const ns_model::Question& q1, const ns_model::Question& q2){
                return atoi(q1.id.c_str()) < atoi(q2.id.c_str());
            });

            // 将所有问题写入到html中
            _view.AllQuestions2html(v, html);
            return true;
        }

        bool OneQuestion(const std::string& id, std::string* html)
        {
            ns_model::Question q;
            if(_model.LoadOneQuestion(id, &q) == false) // 获取指定题目
            {
                // 加载失败
                LOG(FATAL) << "加载问题：" << id << " 失败\n";
                return false;
            }
            
            // 将题目写入到html中
            _view.OneQuestion2html(q, html);
            return true;
        }

        void Judge(std::string& id, const std::string& in_json, std::string* out_json)
        {
            // 对in_json解码
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json, in_value);

            std::string code = in_value["code"].asString();
            Json::Value out_value;
            ns_model::Question q;
            _model.LoadOneQuestion(id, &q);
            out_value["code"] = code + "\n" + q.tail;
            out_value["cpu_limit"] = q.cpu_limit;
            out_value["mem_limit"] = q.mem_limit;
            out_value["input"] = in_value["input"];
            Json::FastWriter writer;
            std::string comile_json = writer.write(out_value);

            // 选择主机
            while(true)
            {
                uint64_t machine_id = 0;
                Machine *m;
                if(!_loadbance.GetBestMachine(&machine_id, &m)) // 获取主机id和信息
                {
                    // 当前没有在线主机
                    _loadbance.OnlineMachine(); // 上线主机
                    sleep(5); // 等待运维，上线主机
                    LOG(INFO) << "上线主机" << "\n";
                }
                httplib::Client cli(m->_ip, m->_port);
                m->IncreaseLoad();
                if(auto res = cli.Post("/compile_and_run", comile_json, "application/json;charset=utf-8")) // 编译运行
                {
                    if(res->status == 200)
                    {
                        LOG(INFO) << "主机 " << m->_ip << ":" << m->_port << " 执行,负载：" << m->GetLoad() << "\n";
                        m->DecreaseLoad();
                        *out_json = res->body; // 返回结果
                        break;
                    }
                }
                else
                {
                    LOG(ERROR) << "主机 " << m->_ip << ":" << m->_port << " 离线" << "\n";
                    _loadbance.OfflineMachine(machine_id);
                }
            }
        }
    
    private:
        ns_model::Model _model; // 数据交互模块
        ns_view::View _view; // 网页交互模块
        LoadBance _loadbance; // 负载均衡模块
    };
}